Skip to main content

Section II Q-3 Alt: Android Animations / Shared Preferences (5 Marks)

Question

a) Describe the different types of animations available in Android

OR

b) Describe the process of creating and managing shared preferences in Android.


Answer A: Android Animations

Overview of Android Animations

Android provides multiple animation systems to create smooth, engaging user interfaces. These animations enhance user experience by providing visual feedback and creating fluid transitions between different states.

Types of Android Animations

1. View Animations (Tween Animations)

Traditional animation system that works with View objects.

Animation Types:

  • Alpha: Fade in/out effects
  • Scale: Size changes
  • Translate: Position changes
  • Rotate: Rotation effects

XML-based View Animations

Alpha Animation (fade_in.xml):

<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:interpolator="@android:anim/accelerate_interpolator" />

Scale Animation (scale_up.xml):

<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="800"
android:fromXScale="0.0"
android:fromYScale="0.0"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:interpolator="@android:anim/bounce_interpolator" />

Translate Animation (slide_in_right.xml):

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="500"
android:fromXDelta="100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%"
android:interpolator="@android:anim/decelerate_interpolator" />

Rotate Animation (rotate_360.xml):

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="infinite"
android:interpolator="@android:anim/linear_interpolator" />

Combined Animation Set (combo_animation.xml):

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">

<alpha
android:duration="1000"
android:fromAlpha="0.0"
android:toAlpha="1.0" />

<scale
android:duration="1000"
android:fromXScale="0.5"
android:fromYScale="0.5"
android:toXScale="1.0"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%" />

<rotate
android:duration="1000"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%" />

</set>

Java Implementation of View Animations

package com.example.animations;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.AlphaAnimation;
import android.view.animation.ScaleAnimation;
import android.view.animation.TranslateAnimation;
import android.view.animation.RotateAnimation;
import android.view.animation.AnimationSet;
import android.widget.Button;
import android.widget.ImageView;

public class ViewAnimationActivity extends Activity {
private ImageView animatedView;
private Button btnFadeIn, btnScaleUp, btnSlideIn, btnRotate, btnCombo;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_animation);

initViews();
setupClickListeners();
}

private void initViews() {
animatedView = findViewById(R.id.animatedView);
btnFadeIn = findViewById(R.id.btnFadeIn);
btnScaleUp = findViewById(R.id.btnScaleUp);
btnSlideIn = findViewById(R.id.btnSlideIn);
btnRotate = findViewById(R.id.btnRotate);
btnCombo = findViewById(R.id.btnCombo);
}

private void setupClickListeners() {
btnFadeIn.setOnClickListener(v -> startFadeInAnimation());
btnScaleUp.setOnClickListener(v -> startScaleAnimation());
btnSlideIn.setOnClickListener(v -> startSlideAnimation());
btnRotate.setOnClickListener(v -> startRotateAnimation());
btnCombo.setOnClickListener(v -> startComboAnimation());
}

// XML-based animations
private void startFadeInAnimation() {
Animation fadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
animatedView.startAnimation(fadeIn);
}

// Programmatic animations
private void startScaleAnimation() {
ScaleAnimation scaleAnimation = new ScaleAnimation(
0.0f, 1.0f, // fromXScale, toXScale
0.0f, 1.0f, // fromYScale, toYScale
Animation.RELATIVE_TO_SELF, 0.5f, // pivotXType, pivotXValue
Animation.RELATIVE_TO_SELF, 0.5f // pivotYType, pivotYValue
);
scaleAnimation.setDuration(800);
scaleAnimation.setFillAfter(true);
animatedView.startAnimation(scaleAnimation);
}

private void startSlideAnimation() {
TranslateAnimation slideAnimation = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 1.0f, // fromXType, fromXValue
Animation.RELATIVE_TO_PARENT, 0.0f, // toXType, toXValue
Animation.RELATIVE_TO_PARENT, 0.0f, // fromYType, fromYValue
Animation.RELATIVE_TO_PARENT, 0.0f // toYType, toYValue
);
slideAnimation.setDuration(500);
slideAnimation.setFillAfter(true);
animatedView.startAnimation(slideAnimation);
}

private void startRotateAnimation() {
RotateAnimation rotateAnimation = new RotateAnimation(
0, 360, // fromDegrees, toDegrees
Animation.RELATIVE_TO_SELF, 0.5f, // pivotXType, pivotXValue
Animation.RELATIVE_TO_SELF, 0.5f // pivotYType, pivotYValue
);
rotateAnimation.setDuration(1000);
rotateAnimation.setRepeatCount(Animation.INFINITE);
animatedView.startAnimation(rotateAnimation);
}

private void startComboAnimation() {
AnimationSet animationSet = new AnimationSet(true);

AlphaAnimation alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(1000);

ScaleAnimation scale = new ScaleAnimation(0.5f, 1.0f, 0.5f, 1.0f,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scale.setDuration(1000);

RotateAnimation rotate = new RotateAnimation(0, 360,
Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(1000);

animationSet.addAnimation(alpha);
animationSet.addAnimation(scale);
animationSet.addAnimation(rotate);

animatedView.startAnimation(animationSet);
}
}

2. Property Animations (Object Animator)

Modern animation system that can animate any property of any object.

package com.example.propertyanimations;

import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;

public class PropertyAnimationActivity extends Activity {
private ImageView targetView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_property_animation);

targetView = findViewById(R.id.targetView);
setupAnimationButtons();
}

private void setupAnimationButtons() {
findViewById(R.id.btnObjectAnimator).setOnClickListener(v -> runObjectAnimator());
findViewById(R.id.btnValueAnimator).setOnClickListener(v -> runValueAnimator());
findViewById(R.id.btnAnimatorSet).setOnClickListener(v -> runAnimatorSet());
findViewById(R.id.btnPropertyValuesHolder).setOnClickListener(v -> runPropertyValuesHolder());
}

// Single property animation
private void runObjectAnimator() {
ObjectAnimator animator = ObjectAnimator.ofFloat(targetView, "translationX", 0f, 300f);
animator.setDuration(1000);
animator.setRepeatCount(1);
animator.setRepeatMode(ObjectAnimator.REVERSE);
animator.start();
}

// Custom value animation
private void runValueAnimator() {
ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
animator.setDuration(2000);
animator.addUpdateListener(animation -> {
float value = (float) animation.getAnimatedValue();
targetView.setAlpha(value);
targetView.setScaleX(value);
targetView.setScaleY(value);
});
animator.start();
}

// Multiple animations in sequence/parallel
private void runAnimatorSet() {
ObjectAnimator moveX = ObjectAnimator.ofFloat(targetView, "translationX", 0f, 200f);
ObjectAnimator moveY = ObjectAnimator.ofFloat(targetView, "translationY", 0f, 200f);
ObjectAnimator rotate = ObjectAnimator.ofFloat(targetView, "rotation", 0f, 360f);
ObjectAnimator scale = ObjectAnimator.ofFloat(targetView, "scaleX", 1f, 2f);

AnimatorSet animatorSet = new AnimatorSet();
animatorSet.play(moveX).with(moveY); // Play together
animatorSet.play(rotate).after(moveX); // Play after
animatorSet.play(scale).with(rotate); // Play together
animatorSet.setDuration(1000);
animatorSet.start();
}

// Multiple properties on single object
private void runPropertyValuesHolder() {
PropertyValuesHolder pvhX = PropertyValuesHolder.ofFloat("translationX", 0f, 300f);
PropertyValuesHolder pvhY = PropertyValuesHolder.ofFloat("translationY", 0f, 300f);
PropertyValuesHolder pvhRotation = PropertyValuesHolder.ofFloat("rotation", 0f, 720f);
PropertyValuesHolder pvhAlpha = PropertyValuesHolder.ofFloat("alpha", 1f, 0.3f, 1f);

ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(
targetView, pvhX, pvhY, pvhRotation, pvhAlpha);
animator.setDuration(2000);
animator.start();
}
}

3. Drawable Animations (Frame Animation)

Sequence of drawable resources displayed in order.

Frame Animation XML (spin_animation.xml):

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">

<item android:drawable="@drawable/frame1" android:duration="100" />
<item android:drawable="@drawable/frame2" android:duration="100" />
<item android:drawable="@drawable/frame3" android:duration="100" />
<item android:drawable="@drawable/frame4" android:duration="100" />
<item android:drawable="@drawable/frame5" android:duration="100" />

</animation-list>

Java Implementation:

public class FrameAnimationActivity extends Activity {
private ImageView animationView;
private AnimationDrawable frameAnimation;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_frame_animation);

animationView = findViewById(R.id.animationView);
animationView.setBackgroundResource(R.drawable.spin_animation);
frameAnimation = (AnimationDrawable) animationView.getBackground();

findViewById(R.id.btnStart).setOnClickListener(v -> {
if (!frameAnimation.isRunning()) {
frameAnimation.start();
}
});

findViewById(R.id.btnStop).setOnClickListener(v -> {
if (frameAnimation.isRunning()) {
frameAnimation.stop();
}
});
}
}

4. Vector Drawable Animations (Animated Vector Drawables)

Animates vector graphics for scalable, smooth animations.

Vector Drawable (ic_heart.xml):

<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">

<path
android:name="heart"
android:fillColor="#FF0000"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36,2,12.28,2,8.5 2,5.42,4.42,3,7.5,3c1.74,0,3.41,0.81,4.5,2.09C13.09,3.81,14.76,3,16.5,3 19.58,3,22,5.42,22,8.5c0,3.78,-3.4,6.86,-8.55,11.54L12,21.35z" />

</vector>

Animation (heart_beat.xml):

<set xmlns:android="http://schemas.android.com/apk/res/android">
<objectAnimator
android:duration="300"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="1.2"
android:repeatCount="1"
android:repeatMode="reverse"
android:interpolator="@android:anim/accelerate_decelerate_interpolator" />

<objectAnimator
android:duration="300"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="1.2"
android:repeatCount="1"
android:repeatMode="reverse"
android:interpolator="@android:anim/accelerate_decelerate_interpolator" />
</set>

Animated Vector Drawable (animated_heart.xml):

<animated-vector xmlns:android="http://schemas.android.com/apk/res/android">
<aapt:attr name="android:drawable">
<vector
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="heart"
android:fillColor="#FF0000"
android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36,2,12.28,2,8.5 2,5.42,4.42,3,7.5,3c1.74,0,3.41,0.81,4.5,2.09C13.09,3.81,14.76,3,16.5,3 19.58,3,22,5.42,22,8.5c0,3.78,-3.4,6.86,-8.55,11.54L12,21.35z" />
</vector>
</aapt:attr>

<target android:name="heart">
<aapt:attr name="android:animation">
<set>
<objectAnimator
android:duration="600"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="1.5"
android:repeatCount="infinite"
android:repeatMode="reverse" />
<objectAnimator
android:duration="600"
android:propertyName="scaleY"
android:valueFrom="1"
android:valueTo="1.5"
android:repeatCount="infinite"
android:repeatMode="reverse" />
</set>
</aapt:attr>
</target>
</animated-vector>

5. Activity and Fragment Transitions

Custom Activity Transitions:

public class TransitionActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_transition);

// Enable window content transitions
getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);

// Set custom transitions
getWindow().setEnterTransition(new Slide(Gravity.RIGHT));
getWindow().setExitTransition(new Fade());
}

private void startActivityWithTransition() {
Intent intent = new Intent(this, SecondActivity.class);

// Create transition bundle
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(
this,
findViewById(R.id.sharedElement),
"shared_element_name"
);

startActivity(intent, options.toBundle());
}
}

Animation Performance and Best Practices

1. Hardware Acceleration

// Enable hardware acceleration for smoother animations
animatedView.setLayerType(View.LAYER_TYPE_HARDWARE, null);

// Disable after animation completes
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
animatedView.setLayerType(View.LAYER_TYPE_NONE, null);
}
});

2. Animation Interpolators

// Different interpolators for different effects
animation.setInterpolator(new AccelerateInterpolator()); // Speed up
animation.setInterpolator(new DecelerateInterpolator()); // Slow down
animation.setInterpolator(new BounceInterpolator()); // Bounce effect
animation.setInterpolator(new OvershootInterpolator()); // Overshoot target

3. Memory Management

// Clear animation references
@Override
protected void onDestroy() {
super.onDestroy();
if (animator != null) {
animator.cancel();
animator = null;
}
}

Animation Types Comparison

Animation TypeUse CasePerformanceComplexity
View AnimationSimple transitionsGoodLow
Property AnimationComplex animationsExcellentMedium
Frame AnimationSprite animationsVariableLow
Vector AnimationScalable graphicsGoodMedium
Activity TransitionsScreen changesGoodHigh

Answer B: Shared Preferences Management

Understanding Shared Preferences

Shared Preferences is Android's mechanism for storing and retrieving small amounts of primitive data as key-value pairs. It's ideal for storing user settings, preferences, and application state information.

Creating and Managing Shared Preferences

1. Basic Shared Preferences Operations

package com.example.sharedpreferences;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class SharedPreferencesActivity extends AppCompatActivity {
private static final String PREFS_NAME = "MyAppPreferences";
private static final String KEY_USERNAME = "username";
private static final String KEY_EMAIL = "email";
private static final String KEY_AGE = "age";
private static final String KEY_IS_PREMIUM = "is_premium";
private static final String KEY_THEME_MODE = "theme_mode";
private static final String KEY_NOTIFICATIONS = "notifications_enabled";

private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;

private EditText etUsername, etEmail, etAge;
private Switch switchPremium, switchNotifications;
private TextView tvDisplay;
private Button btnSave, btnLoad, btnClear;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences);

initializeViews();
initializeSharedPreferences();
setupClickListeners();
loadSavedData();
}

private void initializeViews() {
etUsername = findViewById(R.id.etUsername);
etEmail = findViewById(R.id.etEmail);
etAge = findViewById(R.id.etAge);
switchPremium = findViewById(R.id.switchPremium);
switchNotifications = findViewById(R.id.switchNotifications);
tvDisplay = findViewById(R.id.tvDisplay);
btnSave = findViewById(R.id.btnSave);
btnLoad = findViewById(R.id.btnLoad);
btnClear = findViewById(R.id.btnClear);
}

private void initializeSharedPreferences() {
// Method 1: Using specific preference file
sharedPreferences = getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE);

// Method 2: Using default preferences
// sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);

editor = sharedPreferences.edit();
}

private void setupClickListeners() {
btnSave.setOnClickListener(v -> saveAllData());
btnLoad.setOnClickListener(v -> loadSavedData());
btnClear.setOnClickListener(v -> clearAllData());
}

// Save various data types
private void saveAllData() {
String username = etUsername.getText().toString().trim();
String email = etEmail.getText().toString().trim();
String ageStr = etAge.getText().toString().trim();

if (username.isEmpty() || email.isEmpty()) {
Toast.makeText(this, "Please fill required fields", Toast.LENGTH_SHORT).show();
return;
}

try {
int age = ageStr.isEmpty() ? 0 : Integer.parseInt(ageStr);

// Save string values
editor.putString(KEY_USERNAME, username);
editor.putString(KEY_EMAIL, email);

// Save integer value
editor.putInt(KEY_AGE, age);

// Save boolean values
editor.putBoolean(KEY_IS_PREMIUM, switchPremium.isChecked());
editor.putBoolean(KEY_NOTIFICATIONS, switchNotifications.isChecked());

// Save current timestamp
editor.putLong("last_saved", System.currentTimeMillis());

// Save float value (example)
editor.putFloat("app_version", 1.2f);

// Apply changes asynchronously (recommended)
editor.apply();

// Or commit changes synchronously (blocks UI)
// boolean success = editor.commit();

Toast.makeText(this, "Data saved successfully!", Toast.LENGTH_SHORT).show();
displayCurrentData();

} catch (NumberFormatException e) {
Toast.makeText(this, "Please enter a valid age", Toast.LENGTH_SHORT).show();
}
}

// Load saved data with default values
private void loadSavedData() {
String username = sharedPreferences.getString(KEY_USERNAME, "");
String email = sharedPreferences.getString(KEY_EMAIL, "");
int age = sharedPreferences.getInt(KEY_AGE, 0);
boolean isPremium = sharedPreferences.getBoolean(KEY_IS_PREMIUM, false);
boolean notificationsEnabled = sharedPreferences.getBoolean(KEY_NOTIFICATIONS, true);
long lastSaved = sharedPreferences.getLong("last_saved", 0);
float appVersion = sharedPreferences.getFloat("app_version", 1.0f);

// Update UI with loaded data
etUsername.setText(username);
etEmail.setText(email);
etAge.setText(age > 0 ? String.valueOf(age) : "");
switchPremium.setChecked(isPremium);
switchNotifications.setChecked(notificationsEnabled);

displayCurrentData();

if (!username.isEmpty()) {
Toast.makeText(this, "Data loaded successfully!", Toast.LENGTH_SHORT).show();
}
}

private void displayCurrentData() {
StringBuilder displayText = new StringBuilder();
displayText.append("Current Saved Data:\n\n");

// Get all preferences
Map<String, ?> allPrefs = sharedPreferences.getAll();

for (Map.Entry<String, ?> entry : allPrefs.entrySet()) {
String key = entry.getKey();
Object value = entry.getValue();

displayText.append(key).append(": ").append(value).append("\n");
}

tvDisplay.setText(displayText.toString());
}

private void clearAllData() {
editor.clear();
editor.apply();

// Clear UI
etUsername.setText("");
etEmail.setText("");
etAge.setText("");
switchPremium.setChecked(false);
switchNotifications.setChecked(true);

displayCurrentData();
Toast.makeText(this, "All data cleared!", Toast.LENGTH_SHORT).show();
}

// Remove specific key
private void removeSpecificData(String key) {
editor.remove(key);
editor.apply();

Toast.makeText(this, "Key '" + key + "' removed!", Toast.LENGTH_SHORT).show();
}

// Check if key exists
private boolean containsKey(String key) {
return sharedPreferences.contains(key);
}

// Get all preferences as Map
private Map<String, ?> getAllPreferences() {
return sharedPreferences.getAll();
}
}

2. Advanced Shared Preferences Management

Preference Manager Utility Class:

public class PreferenceManager {
private static final String PREF_NAME = "AppPreferences";
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
private Context context;

// Singleton pattern
private static PreferenceManager instance;

private PreferenceManager(Context context) {
this.context = context.getApplicationContext();
sharedPreferences = this.context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
}

public static synchronized PreferenceManager getInstance(Context context) {
if (instance == null) {
instance = new PreferenceManager(context);
}
return instance;
}

// Generic methods for different data types
public void saveString(String key, String value) {
editor.putString(key, value).apply();
}

public String getString(String key, String defaultValue) {
return sharedPreferences.getString(key, defaultValue);
}

public void saveInt(String key, int value) {
editor.putInt(key, value).apply();
}

public int getInt(String key, int defaultValue) {
return sharedPreferences.getInt(key, defaultValue);
}

public void saveBoolean(String key, boolean value) {
editor.putBoolean(key, value).apply();
}

public boolean getBoolean(String key, boolean defaultValue) {
return sharedPreferences.getBoolean(key, defaultValue);
}

public void saveLong(String key, long value) {
editor.putLong(key, value).apply();
}

public long getLong(String key, long defaultValue) {
return sharedPreferences.getLong(key, defaultValue);
}

public void saveFloat(String key, float value) {
editor.putFloat(key, value).apply();
}

public float getFloat(String key, float defaultValue) {
return sharedPreferences.getFloat(key, defaultValue);
}

// Advanced operations
public void saveStringSet(String key, Set<String> value) {
editor.putStringSet(key, value).apply();
}

public Set<String> getStringSet(String key, Set<String> defaultValue) {
return sharedPreferences.getStringSet(key, defaultValue);
}

public boolean containsKey(String key) {
return sharedPreferences.contains(key);
}

public void removeKey(String key) {
editor.remove(key).apply();
}

public void clearAll() {
editor.clear().apply();
}

public Map<String, ?> getAllPreferences() {
return sharedPreferences.getAll();
}

// Object serialization (using Gson)
public void saveObject(String key, Object object) {
Gson gson = new Gson();
String json = gson.toJson(object);
saveString(key, json);
}

public <T> T getObject(String key, Class<T> classType) {
String json = getString(key, null);
if (json != null) {
Gson gson = new Gson();
return gson.fromJson(json, classType);
}
return null;
}

// Array operations
public void saveStringArray(String key, String[] array) {
Set<String> set = new HashSet<>(Arrays.asList(array));
saveStringSet(key, set);
}

public String[] getStringArray(String key) {
Set<String> set = getStringSet(key, new HashSet<>());
return set.toArray(new String[0]);
}
}

3. Preference Change Listener

public class PreferenceChangeListener implements SharedPreferences.OnSharedPreferenceChangeListener {
private Context context;

public PreferenceChangeListener(Context context) {
this.context = context;
}

@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
// Handle preference changes
switch (key) {
case "theme_mode":
String theme = sharedPreferences.getString(key, "light");
applyTheme(theme);
break;

case "notifications_enabled":
boolean enabled = sharedPreferences.getBoolean(key, true);
toggleNotifications(enabled);
break;

case "language":
String language = sharedPreferences.getString(key, "en");
changeLanguage(language);
break;
}
}

private void applyTheme(String theme) {
// Apply theme changes
}

private void toggleNotifications(boolean enabled) {
// Enable/disable notifications
}

private void changeLanguage(String language) {
// Change app language
}

public void registerListener(SharedPreferences prefs) {
prefs.registerOnSharedPreferenceChangeListener(this);
}

public void unregisterListener(SharedPreferences prefs) {
prefs.unregisterOnSharedPreferenceChangeListener(this);
}
}

4. Preference Categories and Organization

public class OrganizedPreferences {
private PreferenceManager prefManager;

// User preferences
public static class UserPrefs {
public static final String USERNAME = "user_username";
public static final String EMAIL = "user_email";
public static final String PROFILE_IMAGE = "user_profile_image";
public static final String LAST_LOGIN = "user_last_login";
}

// App settings
public static class AppSettings {
public static final String THEME = "app_theme";
public static final String LANGUAGE = "app_language";
public static final String NOTIFICATIONS = "app_notifications";
public static final String AUTO_BACKUP = "app_auto_backup";
}

// Cache settings
public static class CacheSettings {
public static final String CACHE_SIZE = "cache_size";
public static final String CACHE_EXPIRY = "cache_expiry";
public static final String AUTO_CLEAR_CACHE = "auto_clear_cache";
}

public OrganizedPreferences(Context context) {
prefManager = PreferenceManager.getInstance(context);
}

// User preference methods
public void saveUserProfile(String username, String email) {
prefManager.saveString(UserPrefs.USERNAME, username);
prefManager.saveString(UserPrefs.EMAIL, email);
prefManager.saveLong(UserPrefs.LAST_LOGIN, System.currentTimeMillis());
}

public UserProfile getUserProfile() {
String username = prefManager.getString(UserPrefs.USERNAME, "");
String email = prefManager.getString(UserPrefs.EMAIL, "");
long lastLogin = prefManager.getLong(UserPrefs.LAST_LOGIN, 0);

return new UserProfile(username, email, lastLogin);
}

// App settings methods
public void saveAppSettings(String theme, String language, boolean notifications) {
prefManager.saveString(AppSettings.THEME, theme);
prefManager.saveString(AppSettings.LANGUAGE, language);
prefManager.saveBoolean(AppSettings.NOTIFICATIONS, notifications);
}

public AppConfiguration getAppSettings() {
String theme = prefManager.getString(AppSettings.THEME, "light");
String language = prefManager.getString(AppSettings.LANGUAGE, "en");
boolean notifications = prefManager.getBoolean(AppSettings.NOTIFICATIONS, true);

return new AppConfiguration(theme, language, notifications);
}
}

5. Secure Preferences (Encrypted)

import androidx.security.crypto.EncryptedSharedPreferences;
import androidx.security.crypto.MasterKeys;

public class SecurePreferenceManager {
private SharedPreferences encryptedPrefs;
private SharedPreferences.Editor editor;

public SecurePreferenceManager(Context context) throws Exception {
String masterKeyAlias = MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC);

encryptedPrefs = EncryptedSharedPreferences.create(
"secure_prefs",
masterKeyAlias,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

editor = encryptedPrefs.edit();
}

public void saveSecureString(String key, String value) {
editor.putString(key, value).apply();
}

public String getSecureString(String key, String defaultValue) {
return encryptedPrefs.getString(key, defaultValue);
}

// Save sensitive data like passwords, API keys, tokens
public void saveCredentials(String username, String password, String token) {
editor.putString("username", username)
.putString("password", password)
.putString("auth_token", token)
.apply();
}
}

Best Practices for Shared Preferences

1. Performance Optimization

// Use apply() instead of commit() for better performance
editor.putString("key", "value").apply(); // Asynchronous
// editor.putString("key", "value").commit(); // Synchronous (blocks UI)

// Batch multiple operations
editor.putString("key1", "value1")
.putInt("key2", 42)
.putBoolean("key3", true)
.apply();

2. Memory Management

// Don't hold references to SharedPreferences.Editor
private void saveData() {
SharedPreferences.Editor editor = prefs.edit();
editor.putString("key", "value");
editor.apply();
// Editor is automatically garbage collected
}

3. Default Values Strategy

public class DefaultValues {
public static final String DEFAULT_USERNAME = "Guest";
public static final int DEFAULT_AGE = 18;
public static final boolean DEFAULT_NOTIFICATIONS = true;
public static final String DEFAULT_THEME = "light";
}

Shared Preferences Summary

AspectDetails
Storage TypeKey-value pairs
Data TypesString, int, long, float, boolean, StringSet
File Location/data/data/package_name/shared_prefs/
Thread SafetyNot thread-safe (use synchronization if needed)
Size LimitNo specific limit (but should be kept small)
PersistenceSurvives app restarts and updates

← Back to RETEST 2024